<?php
namespace gnomephp\input;

class UploadField{
	
	protected $fileArray=array();
	protected $rules=array();
	protected $messages = array();
	
	public $requiered = false;
	public function __construct($fileArray){
		$this->fileArray = $fileArray;
	}
	
	
	/**
	 * Validation rule.
	 * Maximum size of file (in kilo bytes).
	 * @param string $message
	 */
	public function maxSize($kilo_bytes, $message = null) {
		$message = ( empty ($message) ) ? 'The file size must not exceed '.$kilo_bytes.' KB' : $message;
		$this->addValidaitonRule(__FUNCTION__, function(UploadField $self) use ($kilo_bytes) {
			return $kilo_bytes >= $self->getSize()/1024 ? TRUE : FALSE;
		}, $message);
		return $this;
	}
	
	/**
	 * Validation rule.
	 * Minimum size of file. (in kilo bytes).
	 * @param string $message
	 */
	public function minSize($kilo_bytes, $message = null) {
		$message = ( empty ($message) ) ? 'The file size must not be less then '.$kilo_bytes.' KB' : $message;
		$this->addValidaitonRule(__FUNCTION__, function(UploadField $self) use ($kilo_bytes) {
			return $kilo_bytes <= $self->getSize()/1024 ? TRUE : FALSE;
		}, $message);
		return $this;
	}
	
	
	/**
	 * Validation rule.
	 * Regexp match the filename.
	 * @param string $message
	 */
	public function regexpMatch($pattern, $message = null) {
		$message = ( empty ($message) ) ? 'The file name is illegal. Please justify the filename before uploading.' : $message;
		$this->addValidaitonRule(__FUNCTION__, function(UploadField $self) use ($pattern) {
			return preg_match($pattern, $self->getName()) ? TRUE : FALSE;
		}, $message);
		return $this;
	}
	
	
	/**
	 * Validation rule.
	 * File is required to upload.
	 * @param string $message
	 */
	public function requiered($message = null) {
		$message = ( empty ($message) ) ? 'It is required to upload a file.' : $message;
		$this->addValidaitonRule(__FUNCTION__, function(UploadField $self) {
			$self->requiered = true;
			return $self->getName() != '' && $self->getTmpName() != '' ? TRUE : FALSE;
		}, $message);
		return $this;
	}
	
	
	/**
	 * Validation rule.
	 * Validates only if certain file extension is given.
	 * Provide array or a simple string.
	 * @param string $message
	 */
	public function validFileType($file_extensions, $message = null) {
		$message = ( empty ($message) ) ? 'We do not allow that type of file extension. Allowed file extensions is:'.(is_array($file_extensions) ? implode(' ', $file_extensions) : $file_extensions) : $message;
		$this->addValidaitonRule(__FUNCTION__, function(UploadField $self) use ($file_extensions) {
			if (!is_array($file_extensions) && $self->getType() != $file_extensions)return false;
			if (is_array($file_extensions)){
				if (!in_array($self->getType(), $file_extensions))return false;
			}
			return true;
		}, $message);
		return $this;
	}
	
	/**
	 * Validates rules of file upload if any.
	 * @throws UploadFieldValidException
	 */
	protected function validate(){
		$errors = array();
		
		// Skip if empty and is not requiered.
		if ($this->isEmpty() && !$this->requiered)return $this;
		
		// try each rule function
		foreach ($this->rules as $rule => $function) {
			if (is_callable($function)) {
				if ($function($this) === FALSE) {
					$errors[] = $this->messages[$rule];
				}
			}
		}

		if (count($errors) > 0){
			throw new UploadFieldValidException($errors);
		}

		return $this;
	}
	

	/**
	 * 
	 * Uploads file to $path, and optionaly set a custom filename.
	 * If any validation rules is applied and fails, UploadFieldValidException is thrown.
	 * If any errors regarding upload failure UploadException will be thrown.
	 * 
	 * Returns null if not uploaded ( file was not required in validation rules and was not uploaded ).
	 * @param string $path
	 * @param bool $filename
	 * @throws UploadException
	 * @throws UploadFieldValidException
	 */
	public function upload($path, $filename=null){
		
		// Validate.
		$this->validate();
		
		// If is empty now, we don't upload.
		if ($this->isEmpty())return null;
		
		
		// Throw internal error messages if any this is before it's uploaded to the real location.
		if ($this->getError() !== UPLOAD_ERR_OK)throw new UploadException($this->getError());
		
		
		
		if ($filename === null)$filename = basename($this->getName());
		
		if (!$filename && strstr($filename,'..'))throw new UploadException(UploadException::DOTDOT_FAILURE);
		
		// Generate target path.
		$len = strlen($path);
		$target_path = $path . (substr($path, $len-1, $len)=='/' || substr($path, $len-1, $len) == '\\' ? '' : DIRECTORY_SEPARATOR) . $filename;


		if(move_uploaded_file($this->getTmpName(), $target_path)){
			// OK
			
		}else{
			throw new UploadException(UploadException::MOVE_UPLOAD_FAILURE);
		}
		return $this;
	}
	
	public function isEmpty(){
		return $this->getName() == '' && $this->getTmpName() == '';
	}
	
	/**
	 * Gets the name of the file.
	 */
	public function getName(){
		return $this->fileArray['name'];
	}
	
	/**
	 * Gets the temporary name of the file stored on the server.
	 */
	public function getTmpName(){
		return $this->fileArray['tmp_name'];
	}
	
	/**
	 * Gets the size of the file.
	 */
	public function getSize(){
		return $this->fileArray['size'];
	}
	/**
	 * Gets the filetype of the file.
	 */
	public function getType(){
		return $this->fileArray['type'];
	}
	/**
	 * Gets the error code if any.
	 */
	public function getError(){
		return $this->fileArray['error'];
	}
	
	/**
	 * addValidaitonRule
	 * @param string $rule
	 * @param closure $function
	 * @param string $message
	 */

	public function addValidaitonRule($rule, $function, $message='') {
		if (is_callable($function)) {
			$this->rules[$rule] = $function;
			if (!empty($message)) {
				$this->messages[$rule] = $message;
			}
		}
		return $this;
	}
	
	
	
}